/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.stfalcon.imageviewer.common.extensions;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.PageSlider;
import ohos.agp.utils.Rect;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

/**
 * Extensions function for component.
 *
 * @since 2021-07-16
 */
public class ViewExt {
    private ViewExt() {
    }

    /**
     * The localVisibleRect of component.
     *
     * @param view component
     * @return Rect
     */
    public static Rect localVisibleRect(Component view) {
        Rect rect = new Rect();
        view.getSelfVisibleRect(rect);
        return rect;
    }

    /**
     * The hitRect of component.
     *
     * @param view component
     * @return Rect
     */
    public static Rect hitRect(Component view) {
        Rect rect = new Rect();
        view.getSelfVisibleRect(rect);
        return rect;
    }

    /**
     * The globalVisibleRect of component.
     *
     * @param view component
     * @return Rect
     */
    public static Rect globalVisibleRect(Component view) {
        Rect rect = new Rect();
        view.getWindowVisibleRect(rect);
        return rect;
    }

    /**
     * The component isRectVisible.
     *
     * @param view component
     * @return isRectVisible
     */
    public static boolean isRectVisible(Component view) {
        return globalVisibleRect(view) != localVisibleRect(view);
    }

    /**
     * The component isVisible.
     *
     * @param view component
     * @return isVisible
     */
    public static boolean isVisible(Component view) {
        return view.getVisibility() == Component.VISIBLE;
    }

    /**
     * Make the component visible.
     *
     * @param view component
     */
    public static void makeVisible(Component view) {
        view.setVisibility(Component.VISIBLE);
    }

    /**
     * Make the component invisible.
     *
     * @param view component
     */
    public static void makeInvisible(Component view) {
        view.setVisibility(Component.INVISIBLE);
    }

    /**
     * Make the component hide.
     *
     * @param view component
     */
    public static void makeGone(Component view) {
        view.setVisibility(Component.HIDE);
    }

    /**
     * Kotlin callback.
     *
     * @since 2021-07-16
     */
    public interface Block {
        /**
         * The action of callback
         *
         * @param view component
         */
        void action(Component view);
    }

    /**
     * Post action.
     *
     * @param view component
     * @param crossinline callback
     */
    public static void postApply(Component view, Block crossinline) {
        new EventHandler(EventRunner.getMainEventRunner()).postTask(() -> crossinline.action(view));
    }

    /**
     * Post action.
     *
     * @param view component
     * @param delayMillis action delay
     * @param crossinline callback
     */
    public static void postDelayed(Component view, long delayMillis, Block crossinline) {
        new EventHandler(EventRunner.getMainEventRunner()).postTask(() -> crossinline.action(view), delayMillis);
    }

    /**
     * Set the margin of component.
     *
     * @param view component
     * @param start startMargin
     * @param top topMargin
     * @param end endMargin
     * @param bottom bottomMargin
     */
    public static void applyMargin(Component view, int start, int top, int end, int bottom) {
        ComponentContainer.LayoutConfig layoutConfig;
        if (view.getLayoutConfig() != null) {
            layoutConfig = view.getLayoutConfig();
        } else {
            layoutConfig = new ComponentContainer.LayoutConfig();
        }
        layoutConfig.setMargins(start, top, end, bottom);
        view.setLayoutConfig(layoutConfig);
    }

    /**
     * Set the size of component.
     *
     * @param view component
     * @param width the width px
     * @param height the height px
     */
    public static void requestNewSize(Component view, int width, int height) {
        ComponentContainer.LayoutConfig layoutConfig;
        if (view.getLayoutConfig() != null) {
            layoutConfig = view.getLayoutConfig();
        } else {
            layoutConfig = new ComponentContainer.LayoutConfig();
        }
        layoutConfig.width = width;
        layoutConfig.height = height;
        view.setLayoutConfig(layoutConfig);
    }

    /**
     * Set the component match parent.
     *
     * @param view component
     */
    public static void makeViewMatchParent(Component view) {
        applyMargin(view, 0, 0, 0, 0);
        requestNewSize(view, ComponentContainer.LayoutConfig.MATCH_PARENT,
            ComponentContainer.LayoutConfig.MATCH_PARENT);
    }

    /**
     * Play alpha animation.
     *
     * @param view component
     * @param from alpha from
     * @param to alpha to
     * @param duration animation duration
     */
    public static void animateAlpha(Component view, float from, float to, long duration) {
        AnimatorProperty animatorProperty = new AnimatorProperty(view);
        animatorProperty.alphaFrom(from).alpha(to).setDuration(duration).start();
    }

    /**
     * Reverse the visible state by animation.
     *
     * @param view component
     */
    public static void switchVisibilityWithAnimation(Component view) {
        boolean isVisible = view.getVisibility() == Component.VISIBLE;
        float from = isVisible ? 1f : 0f;
        float to = isVisible ? 0f : 1f;
        AnimatorProperty animatorProperty = new AnimatorProperty(view);
        if (isVisible) {
            animatorProperty.setStateChangedListener(new AnimatorListenerAdapter() {
                @Override
                public void onEnd(Animator animator) {
                    makeGone(view);
                }
            });
        } else {
            makeVisible(view);
        }
        final long duration = 300L;
        animatorProperty.alphaFrom(from).alpha(to).setDuration(duration).start();
    }

    /**
     * Adapter for StateChangedListener.
     *
     * @since 2021-07-16
     */
    public static class AnimatorListenerAdapter implements Animator.StateChangedListener {
        @Override
        public void onStart(Animator animator) {
        }

        @Override
        public void onStop(Animator animator) {
        }

        @Override
        public void onCancel(Animator animator) {
        }

        @Override
        public void onEnd(Animator animator) {
        }

        @Override
        public void onPause(Animator animator) {
        }

        @Override
        public void onResume(Animator animator) {
        }
    }

    /**
     * Adapter for PageChangedListener.
     *
     * @since 2021-07-16
     */
    public static class PageSliderChangedAdapter implements PageSlider.PageChangedListener {
        @Override
        public void onPageSliding(int itemPos, float itemPosOffset, int itemPosOffsetPixels) {
        }

        @Override
        public void onPageSlideStateChanged(int state) {
        }

        @Override
        public void onPageChosen(int itemPos) {
        }
    }
}
